package org.stvd.core.web;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import javax.servlet.http.HttpServletRequest;

import org.stvd.core.util.StringUtil;

public class IPUtil {

    /*
     * 获取IP地址
     */
    public static String[] getIpAddrArray(HttpServletRequest request) {
        String ip = null;
        try {
            ip = request.getHeader("X-Forwarded-For");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("X-Real-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            if (!StringUtil.isEmpty(ip)) {
                return ip.split(",");
            }
        } catch (Exception ex) {

        }
        return null;
    }

    public static String getIpAddr(HttpServletRequest request) {
        String[] ipArray = getIpAddrArray(request);
        if (ipArray != null) {
            return ipArray[0];
        }
        return "";
    }

    /*
     * 获取IP地址，在log4j中，可以通过配置获得 %X{ip}
     */
    public static String getIP(HttpServletRequest request) {
        String[] ipArray = IPUtil.getIpAddrArray(request);
        if (ipArray != null && ipArray.length > 0) {
            StringBuilder ipResult = new StringBuilder();
            for (String ip : ipArray) {
                ipResult.append(ip);
                if (ipArray.length > 1) {
                    ipResult.append(';');
                }
            }
            return ipResult.toString();
        }
        return "";
    }

    public static final boolean enableFileWatch = false;

    private static int offset;
    private static int[] index = new int[256];
    private static ByteBuffer dataBuffer;
    private static ByteBuffer indexBuffer;
    private static Long lastModifyTime = 0L;
    private static File ipFile;
    private static ReentrantLock lock = new ReentrantLock();
    static {
        IPUtil.load(IPUtil.class.getResource("/").getPath() + "17monipdb.dat");
    }

    public static void load(String filename) {
        ipFile = new File(filename);
        load();
        if (enableFileWatch) {
            watch();
        }
    }

    public static void load(String filename, boolean strict) throws Exception {
        ipFile = new File(filename);
        if (strict) {
            int contentLength = Long.valueOf(ipFile.length()).intValue();
            if (contentLength < 512 * 1024) {
                throw new Exception("ip data file error.");
            }
        }
        load();
        if (enableFileWatch) {
            watch();
        }
    }

    public static String[] find(String ipAddress) {
        String ip = ipAddress.trim();
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        int ipPrefixValue = Integer.parseInt(ip.substring(0, ip.indexOf('.')));
        long ip2longValue = ip2long(ip);
        int start = index[ipPrefixValue];
        int maxCompLen = offset - 1028;
        long indexOffset = -1;
        int indexLength = -1;
        byte b = 0;
        for (start = start * 8 + 1024; start < maxCompLen; start += 8) {
            if (int2long(indexBuffer.getInt(start)) >= ip2longValue) {
                indexOffset = bytesToLong(b, indexBuffer.get(start + 6), indexBuffer.get(start + 5),
                        indexBuffer.get(start + 4));
                indexLength = 0xFF & indexBuffer.get(start + 7);
                break;
            }
        }

        byte[] areaBytes;

        lock.lock();
        try {
            dataBuffer.position(offset + (int) indexOffset - 1024);
            areaBytes = new byte[indexLength];
            dataBuffer.get(areaBytes, 0, indexLength);
        } finally {
            lock.unlock();
        }

        return new String(areaBytes, Charset.forName("UTF-8")).split("\t", -1);
    }

    private static void watch() {
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                long time = ipFile.lastModified();
                if (time > lastModifyTime) {
                    lastModifyTime = time;
                    load();
                }
            }
        }, 1000L, 5000L, TimeUnit.MILLISECONDS);
    }

    private static void load() {
        lastModifyTime = ipFile.lastModified();
        FileInputStream fin = null;
        lock.lock();
        try {
            dataBuffer = ByteBuffer.allocate(Long.valueOf(ipFile.length()).intValue());
            fin = new FileInputStream(ipFile);
            int readBytesLength;
            byte[] chunk = new byte[4096];
            while (fin.available() > 0) {
                readBytesLength = fin.read(chunk);
                dataBuffer.put(chunk, 0, readBytesLength);
            }
            dataBuffer.position(0);
            int indexLength = dataBuffer.getInt();
            byte[] indexBytes = new byte[indexLength];
            dataBuffer.get(indexBytes, 0, indexLength - 4);
            indexBuffer = ByteBuffer.wrap(indexBytes);
            indexBuffer.order(ByteOrder.LITTLE_ENDIAN);
            offset = indexLength;

            int loop = 0;
            while (loop < 256) {
                loop++;
                index[loop - 1] = indexBuffer.getInt();
            }
            indexBuffer.order(ByteOrder.BIG_ENDIAN);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            lock.unlock();
            try {
                if (fin != null) {
                    fin.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static long bytesToLong(byte a, byte b, byte c, byte d) {
        return int2long(((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff));
    }

    private static int str2Ip(String ip) {
        String[] ss = ip.split("\\.");
        int a = Integer.parseInt(ss[0]);
        int b = Integer.parseInt(ss[1]);
        int c = Integer.parseInt(ss[2]);
        int d = Integer.parseInt(ss[3]);
        return (a << 24) | (b << 16) | (c << 8) | d;
    }

    private static long ip2long(String ip) {
        return int2long(str2Ip(ip));
    }

    private static long int2long(int i) {
        long l = i & 0x7fffffffL;
        if (i < 0) {
            l |= 0x080000000L;
        }
        return l;
    }

    /*
     * 获取MAC地址
     */
    public static String getMACAddress(String ipAddress) {
        String str = "";
        String strMAC = "";
        String macAddress = "";
        InputStreamReader ir = null;
        LineNumberReader input = null;
        try {
            Process pp = Runtime.getRuntime().exec("nbtstat -a " + ipAddress);
            ir = new InputStreamReader(pp.getInputStream(), "UTF-8");
            input = new LineNumberReader(ir);
            for (int i = 1; i < 100; i++) {
                str = input.readLine();
                if (str != null && str.indexOf("MAC Address") > 1) {
                    strMAC = str.substring(str.indexOf("MAC Address") + 14, str.length());
                }
            }
        } catch (IOException ex) {
            return "Can't Get MAC Address!";
        } finally {
            if (ir != null) {
                try {
                    ir.close();
                    ir = null;
                } catch (IOException e) {
                }
            }
            if (input != null) {
                try {
                    input.close();
                    input = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //
        if (strMAC.length() < 17) {
            return "Error!";
        }
        macAddress = strMAC.substring(0, 2) + ":" + strMAC.substring(3, 5) + ":" + strMAC.substring(6, 8) + ":"
                + strMAC.substring(9, 11) + ":" + strMAC.substring(12, 14) + ":" + strMAC.substring(15, 17);
        //
        return macAddress;
    }

    public static String getHostName(String clientIP) {
        String s = "";
        String sb = clientIP.trim().substring(0, 3);
        // System.out.println("clientIP="+clientIP+"\t"+"截取字符串为："+sb);
        if ("127".equals(sb)) {
            s = "本机";
        } else {
            try {
                String s1 = "nbtstat -a " + clientIP;
                Process process = Runtime.getRuntime().exec(s1);
                BufferedReader bufferedreader = new BufferedReader(
                        new InputStreamReader(process.getInputStream(), "UTF-8"));
                String nextLine;
                int y = 0;
                for (String line = bufferedreader.readLine(); line != null; line = nextLine) {
                    nextLine = bufferedreader.readLine();
                    if (y == 13) {
                        // 截取字符串
                        s = nextLine.substring(0, nextLine.indexOf("<00>")).toLowerCase();
                    }
                    y++;
                }
                bufferedreader.close();
                process.waitFor();
            } catch (Exception exception) {
                s = "";
                exception.printStackTrace();
            }
        }
        return s.trim();
    }

    /*
     * 获取windows下cpu序列号
     */
    public static String getWinCpuId() {
        Process process;
        try {
            process = Runtime.getRuntime().exec(new String[] { "wmic", "cpu", "get", "ProcessorId" });
            process.getOutputStream().close();
            @SuppressWarnings("resource")
            Scanner sc = new Scanner(process.getInputStream(), "UTF-8");
            sc.next();
            return sc.next();
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}